2006年01月29日
川俣晶の縁側ソフトウェア技術雑記 total 3731 count

.NET Framework 2.0のXmlWriterSettings.CloseOutputプロパティの既定値はXmlWriter.Createメソッドの引数タイプによって変化する!?

Written By: 川俣 晶連絡先

 .NET Framework 2.0で、XmlWriterを使ってXML文書を書き出す場合には、XmlTextWriterクラスを使わず、XmlWriter.Createメソッドでインスタンスを取得する方法が推奨されています。

 また、このメソッドの引数に指定できるXmlWriterSettingsメソッドは、きめ細かい動作のオプションを指定することを可能としています。

 このうちのXmlWriterSettings.CloseOutputプロパティは、XmlWriterのCloseメソッドを呼んだときに、元になるストリームも閉じるかを指定します。これがfalseの場合、XmlWriterをCloseしたあと、ストリームに何かを書き足すことが可能になります。たとえば、MIMEマルチパートの電子メール本文を書き出す場合などには便利そうですね。

 このプロパティの既定値はfalseです。つまり、Closeメソッドを呼び出しても、元になるストリームは閉じられません。

 以上が前提です。

ファイル名を指定するCreateメソッド §

 XmlWriter.Createメソッドには、引数によって様々なバリエーションがあります。その多くは、XmlWriterSettingsオブジェクトを渡すバリエーションと省略して既定値で使うバリエーションが存在します。

 さて、ここでファイル名のみを指定してそれに対して出力するXmlWriterオブジェクトを生成するバリエーションについて見ていきましょう。

 以下のように使うわけです。

XmlWriter writer = XmlWriter.Create(@"c:\\test.xml");

writer.WriteElementString("test", "data");

writer.Close();

 さて、ここで注意してください。

 このケースでは、明示的にXmlWriterSettingsを渡していません。おそらく、既定値で使われていることが予想されます。

 そして、XmlWriterSettings.CloseOutputプロパティの既定値はfalseです。

 ということは、Closeメソッドを呼び出しても、書き込みに使われるストリームは閉じられません。

 しかし、このプログラムでは、ストリームを閉じるコードが含まれません。というか、そもそもXmlWriterというのは抽象的なクラスなので、ストリームを取得する手段が提供されていません。

 (もちろん、抽象的なクラスのインスタンスは作成できないので、実は取得されているのは非公開クラスらしきSystem.Xml.XmlWellFormedWriterクラスのインスタンス)

 ということは、ファイル名のみを指定するCreateメソッドを使うと永遠にファイルを閉じることができない?

 つまり、ファイル名のみを指定するCreateメソッドには存在意義がない?

実はそうではないらしい §

 実はXmlWriterSettings.CloseOutputプロパティの既定値はfalseだという記述は必ずしも正確ではないようです。

 というのは、実はファイル名のみを指定するCreateメソッドを使うと、このプロパティの値はtrueになってしまうからです。

 検証プログラムで確かめてみました。

検証プログラム §

 .NET Framework 2.0+C#, Visual Studio 2005で作成

using System;

using System.Collections.Generic;

using System.Text;

using System.Xml;

using System.IO;

namespace ConsoleApplication75

{

    class Program

    {

        static void Main(string[] args)

        {

            XmlWriter writer = XmlWriter.Create(@"c:\\test.xml");

            Console.WriteLine("writer.Settings.CloseOutput={0}", 

                writer.Settings.CloseOutput);

            Console.WriteLine(writer.ToString());

            writer.WriteElementString("test", "data");

            writer.Close();

            FileStream stream = new FileStream(@"c:\\test.xml", 

                FileMode.Create);

            XmlWriter writer2 = XmlWriter.Create(stream);

            Console.WriteLine("writer2.Settings.CloseOutput={0}", 

                writer2.Settings.CloseOutput);

            Console.WriteLine(writer2.ToString());

            writer2.WriteElementString("test", "data");

            writer2.Close();

            // あ、streamをCloseするのを忘れているぞ…… (汗

        }

    }

}

実行結果 §

writer.Settings.CloseOutput=True

System.Xml.XmlWellFormedWriter

writer2.Settings.CloseOutput=False

System.Xml.XmlWellFormedWriter

CloseOutputプロパティの値は自動的に補正される §

 結果を見て分かる通り、ファイル名だけを渡した場合と、ストリームだけを渡した場合では、CloseOutputプロパティの値は異なっています。

 つまり、後からストリームを閉じることができないケースについては、自動的にCloseOutputプロパティの値がtrueに補正されるようです。

 つまり、ファイル名のみを指定するCreateメソッドには存在意義はあります。

XmlWellFormedWriterって何だろう? §

 googleさんに質問しても、19件しかヒットしない謎のクラスです。

 Createというファクトリ・メソッドで詳細を隠蔽したのだから触るな……、ということでしょうが、たぶんそれで済まないこともあるでしょう。